package com.eg366.selfFrame.core;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.eg366.selfFrame.advisor.SelfAdvisor;
import com.eg366.selfFrame.annotation.Bean;
import com.eg366.selfFrame.annotation.DoWired;

public class SelfBeanFactory {

	private static final Logger logger = LoggerFactory.getLogger(SelfBeanFactory.class);

	/** 存在的beanName集合 */
	private Set<String> beanNameSet = new HashSet<String>();
	/** 待注册为bean的beanName集合，注册完成后清空 */
	private Set<String> unRegistedBeanClassSet = new HashSet<String>();
	/** 单例的bean对象容器 */
	private Map<String, Object> singletonBeanMap = new ConcurrentHashMap<String, Object>();

	private AopFactoryBean aopFactoryBean;

	public SelfBeanFactory() {
		aopFactoryBean = AopFactoryBean.getInstance();
	}

	public void initAopAdvisor(SelfAdvisor... selfAdvisors) {
		aopFactoryBean.initAopAdvisor(selfAdvisors);
	}

	/**
	 * 指定bean的报路径进行扫描，注册
	 */
	public synchronized void scanPackage(String... packageNames) {
		for (String pName : packageNames) {
			scanPackageByName(pName);
		}
		logger.info("regist singleton bean count {}", singletonBeanMap.size());
		logger.info("beanNameSet count {}", beanNameSet.size());
		logger.info("unRegistedBeanClassSet count {}", unRegistedBeanClassSet.size());
	}

	private void scanPackageByName(String packageName) {
		try {
			URL url = Thread.currentThread().getContextClassLoader().getResource(packageName.replace(".", "/"));
			if ("file".equals(url.getProtocol())) {
				File file = new File(url.getFile());
				File[] subFiles = file.listFiles();
				for (File subFile : subFiles) {
					/* 如果是文件夹，再遍历子package */
					if (subFile.isDirectory()) {
						scanPackageByName(packageName + subFile.getName());
					}
					/* 申请待注册的class文件 */
					justIsBean(packageName, subFile);
				}
				// /* 获取包下所有的class文件 */
				// File[] classFiles = file.listFiles(new FilenameFilter() {
				// @Override
				// public boolean accept(File dir, String name) {
				// return name.endsWith(".class");
				// }
				// });
			}
			/* 对bean进行注册 */
			registUnRegistedBean();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 申请待注册的class文件
	 */
	private void justIsBean(String packageName, File... classFiles) throws ClassNotFoundException {
		for (File file : classFiles) {
			String className = packageName + "." + file.getName().replace(".class", "");
			isRegist(className);
		}
	}

	/**
	 * 判断是否声明为待注册的bean对象<br>
	 * 如果声明为注册bean，添加到beanNameSet和unRegistBeanClassSet集合中
	 */
	private boolean isRegist(String className) throws ClassNotFoundException {
		boolean regist = false;
		try {
			Class<?> clazz = Class.forName(className);
			regist = clazz.getAnnotation(Bean.class) != null;
		} catch (ClassNotFoundException e) {
			logger.error("class not found {}" + className);
			throw e;
		}
		if (regist) {
			beanNameSet.add(className);
			unRegistedBeanClassSet.add(className);
		}
		return regist;
	}

	/**
	 * 进行bean的注册，对unRegistBeanClassSet集合中的bean进行regist
	 */
	private void registUnRegistedBean() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		Set<String> copySet = new HashSet<String>();
		copySet.addAll(unRegistedBeanClassSet);
		for (String className : copySet) {
			registBean(className);
			unRegistedBeanClassSet.remove(className);
		}
	}

	private void registBean(String className) throws ClassNotFoundException, InstantiationException,
			IllegalAccessException {
		registBean(Class.forName(className));
	}

	private void registBean(Class<?> beanClass) throws ClassNotFoundException, InstantiationException,
			IllegalAccessException {
		/* 校验是否满足创建bean对象的条件 */
		validateBeanCanCreate(beanClass);
		/* 执行bean的注册 */
		doRegistBean(beanClass);
	}

	private Object doRegistBean(Class<?> beanClass) throws InstantiationException, IllegalAccessException {
		Object object = beanClass.newInstance();
		/* 遍历所有成员变量，对于添加了DoWired注解属性进行处理 */
		for (Field field : beanClass.getDeclaredFields()) {
			DoWired wiredAnno = field.getAnnotation(DoWired.class);
			/* 声明了DoWired注解 */
			if (wiredAnno != null) {
				Bean beanAnno = field.getType().getAnnotation(Bean.class);
				Object fieldObj = null;
				/* 声明注入的bean是否为单例且尚未注册，进行bean的注册 */
				if (beanAnno.isSingleton()) {
					if (!singletonBeanMap.containsKey(beanAnno.id())) {
						/* 创建该成员变量bean */
						fieldObj = doRegistBean(field.getType());
					} else {
						fieldObj = singletonBeanMap.get(beanAnno.id());
					}
				} else {
					/* 非单例，new该对象 */
					fieldObj = field.getType().newInstance();
				}
				field.setAccessible(true);
				field.set(object, fieldObj);
			}
		}

		/* 如果声明为aop，创建动态代理对象 */
		if (isAopBean(beanClass)) {
			object = aopFactoryBean.getObject(object);
		}

		Bean beanAnno = beanClass.getAnnotation(Bean.class);
		if (beanAnno.isSingleton()) {
			singletonBeanMap.put(beanAnno.id(), object);
		}
		return object;
	}

	/**
	 * 校验是否满足创建bean对象的条件
	 */
	private void validateBeanCanCreate(Class<?> beanClass) throws ClassNotFoundException {
		Field[] fields = beanClass.getDeclaredFields();
		for (Field field : fields) {
			DoWired wiredAnno = field.getAnnotation(DoWired.class);
			/* 声明了DoWired注解 */
			if (wiredAnno != null) {
				Class<?> fieldClass = field.getType();
				/* 是否存在 声明注入对象的bean */
				if (!beanNameSet.contains(fieldClass.getName())) {
					logger.error("do not find declared bean {}", field.getName());
					throw new ClassNotFoundException();
				}
			}
		}
	}

	public Object getBean(String beanName) {
		Object object = singletonBeanMap.get(beanName);
		if (object == null)
			throw new RuntimeException("do not have bean " + beanName);
		return object;
	}

	/**
	 * 判断是否为aop对象：声明Bean注解，且doAop为true
	 */
	private boolean isAopBean(Class<?> t) {
		Bean beanAnno = t.getAnnotation(Bean.class);
		return beanAnno != null && beanAnno.doAop();
	}
}
